package com.zjp.aop.aspect;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.zjp.aop.annotation.Log;
import com.zjp.aop.core.LogContext;
import com.zjp.aop.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

//@Aspect
//@Component
public class LogAspect extends CommonAspect implements InitializingBean {


    private final ExpressionParser parser = new SpelExpressionParser();

    private final StandardReflectionParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer();

    // 字段
    private final Map<Method, List<String>> methodField = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.zjp.aop.annotation.Log)")
    public void logPointCut() {

    }


    @Before(value = "logPointCut()")
    public void startLog(final JoinPoint joinpoint) {
        Log annotation = getAnnotation(joinpoint, Log.class);
        if (Objects.nonNull(annotation)) {
            LogContext.setCurrentVariableMap();
        }
    }


    @After(value = "logPointCut()")
    public void logRecord(final JoinPoint joinpoint) throws Throwable {

        Log annotation = getAnnotation(joinpoint, Log.class);
        // "userId:“#obj.userId” dataLogId:“#obj.dataLogId” "
        if (Objects.nonNull(annotation)) {

            String bizNo = Objects.toString(LogContext.getCurrentVariableMap().get(annotation.bizNo()), null);


            String content = annotation.content();

            Object[] args = joinpoint.getArgs();

            Method method = getMethod(joinpoint);


            String finalContent = content;

            List<String> list = methodField.computeIfAbsent(method, (String) -> initMethodField(finalContent));

            StandardEvaluationContext context = this.bindParam(method, args);
            for (String s : list) {
                Expression expression = parser.parseExpression(s);

                Object key = expression.getValue(context);
                //打印
                if (Objects.nonNull(key)) {
                    content = content.replace(StrUtil.wrap(s, "[", "]"), key.toString());
                }
            }
            System.out.println("bizNo----" + bizNo + "---log----跟踪的参数为:" + content);
        }

        LogContext.removeCurrentVariableMap();
    }

    private StandardEvaluationContext bindParam(Method method, Object[] args) {
        //获取方法的参数名
        String[] params = discoverer.getParameterNames(method);
        StandardEvaluationContext context = new StandardEvaluationContext();
        if (MapUtil.isNotEmpty(customize)) {
            for (Map.Entry<String, Method> entry : customize.entrySet()) {
                context.registerFunction(entry.getKey(), entry.getValue());
            }
        }
        if (ArrayUtil.isEmpty(params)) {
            return context;
        }
        //将参数名与参数值对应起来
        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], args[len]);
        }


        return context;
    }


    public List<String> initMethodField(String content) {
        List<String> list = new ArrayList<>();
        String[] strings = StrUtil.subBetweenAll(content, "[", "]");
        for (String string : strings) {
            if (StrUtil.startWith(string, "#")) {
                //String subAfter = StrUtil.subAfter(string, "#", false);
                list.add(string);
                System.out.println(string);
            }
        }
        return list;
    }


    public Map<String, Method> customize = new HashMap<>();

    @Override
    public void afterPropertiesSet() throws Exception {
        customize.put("addOne", LogService.class.getDeclaredMethod("addOne", String.class));
    }
}
